package vh.sphynx.runtime.interpreter;

import java.lang.reflect.Array;

import vh.sphynx.runtime.ExecutionContext;
import vh.sphynx.runtime.SphynxInterpreter;
import vh.sphynx.runtime.TypeConverter;
import vh.sphynx.runtime.VariableStorage;
import vh.sphynx.runtime.interpreter.XFunctionDeclartationInterpreter.FunctionReturn;
import vh.sphynx.sphynx.IdentifiedElement;
import vh.sphynx.sphynx.XExpression;
import vh.sphynx.sphynx.XForControlVariable;
import vh.sphynx.sphynx.XReturnStatement;
import vh.sphynx.sphynx.impl.XForControlVariableImpl;
import vh.sphynx.sphynx.impl.XForExpressionImpl;
import vh.sphynx.sphynx.impl.XIfExpressionImpl;
import vh.sphynx.sphynx.impl.XWhileExpressionImpl;
import vh.sphynx.util.SphynxRuntimeException;

public class XControlFlowInterpreter {
	
	public static Object _evaluate(SphynxInterpreter interpreter, XIfExpressionImpl exp, ExecutionContext context) {
		boolean condition = TypeConverter.convertToBoolean(interpreter, context, exp.getCondition());		
		if (condition)
			return interpreter.evaluate(exp.getIfTrue(), context);
		else if (exp.getIfFalse() != null)
			return interpreter.evaluate(exp.getIfFalse(), context);
		return null;
	}
	
	public static Object _evaluate(SphynxInterpreter interpreter, XWhileExpressionImpl exp, ExecutionContext context) {
		Object result = null;
		ExecutionContext whileContext = context.createDependentContext();
		while(TypeConverter.convertToBoolean(interpreter, context, exp.getCondition())) {
			whileContext.clearVariables();
			result = interpreter.evaluate(exp.getBody(), whileContext);
		}
		return result;
	}
	
	@SuppressWarnings("rawtypes")
	public static Object _evaluate(SphynxInterpreter interpreter, XForExpressionImpl exp, ExecutionContext context) {
		Object result = null;
		context = context.createDependentContext();
		
		Object control = interpreter.evaluate(exp.getForVar(), context);
		
		if (control instanceof VariableStorage && ((VariableStorage) control).isWritable()) {
			VariableStorage controlVar = (VariableStorage) control;
			if (exp.isForEach()) {
				Object list = interpreter.resolveValue(exp.getList(), context);
				ExecutionContext bodyContext = context.createDependentContext();
				
				if (list instanceof Iterable) {	
					for(Object item: (Iterable)list) {						
						controlVar.setValue(item);
						bodyContext.clearVariables();
						result = interpreter.evaluate(exp.getBody(), bodyContext);
						if (result instanceof FunctionReturn)
							if (((FunctionReturn) result).Type == FunctionReturn.BREAK) {
								result = null;
								break;
							}
							else if (((FunctionReturn) result).Type == FunctionReturn.RETURN)
								return result;
					}	
				}
				else if (list instanceof Object[]) {
					for (int i = 0; i < Array.getLength(list); i++) {
						controlVar.setValue(Array.get(list, i));
						bodyContext.clearVariables();
						result = interpreter.evaluate(exp.getBody(), bodyContext);
						if (result instanceof FunctionReturn)
							if (((FunctionReturn) result).Type == FunctionReturn.BREAK) {
								result = null;
								break;
							}
							else if (((FunctionReturn) result).Type == FunctionReturn.RETURN)
								return result;
					}
				}	
			}
			else {
				ExecutionContext bodyContext = context.createDependentContext();
				while((Boolean)interpreter.resolveValue(exp.getCondition(), context, Boolean.class)) {
					bodyContext.clearVariables();
					result = interpreter.evaluate(exp.getBody(), bodyContext);
					if (result instanceof FunctionReturn)
						if (((FunctionReturn) result).Type == FunctionReturn.BREAK) {
							result = null;
							break;
						}
						else if (((FunctionReturn) result).Type == FunctionReturn.RETURN)
							return result;
					// executing post condition statement
					for(XExpression pe: exp.getPostFors())
						interpreter.evaluate(pe, context);
				}
			}
		}
		else
			throw new SphynxRuntimeException(context, exp, "The control variable is not writable");
		return result;
	}
	
	public static Object _evaluate(SphynxInterpreter interpreter, XForControlVariableImpl exp, ExecutionContext context) {
		if (exp.isNew()) {
			Object value = exp.getValue() != null ? interpreter.resolveValue(exp.getValue(), context) : VariableStorage.UNEVALUATED;
			VariableStorage var = context.allocateVariable(VariableStorage.VARIABLE, false, exp.getName(), value);
			return var;
		}
		else 
			return interpreter.evaluate(exp.getFeature(), context);
	}
}
